Generally, computer applications run by executing code. The code controls the actions of the computer systems on which it is run. In order to protect the application from unauthorized tampering with the code, a number of security precautions may be utilized.
Some of these security measures are physical. For example, a user purchasing software on a CD-ROM may be able to verify that the CD-ROM is a legitimate copy of the software by inspecting holograms or other security devices on the packaging.
Module authentication is a security measure that executes on a computer system and attempts to ensure the security of software applications by providing a level of protection against tampering.
Static module authentication is a mechanism to detect tampering of the portable executable binary modules that comprise a software application. These binary modules are stored on the persistent storage device, which is usually a file on a hard disk and therefore often referred to as the “on-disk” image of the module. Authentication of the on-disk image may be accomplished by cryptographically hashing the file and comparing the result to a precomputed hash of the file. A cryptographic hashing algorithm operates on binary data of arbitrary size and generates a fixed size value known as a hash. Cryptographic hashes are computed such that any modification of the hashed data will cause a change in the generated hash. A hash is computed of the on-disk module. It is almost completely impossible to make a modification of a file to insert malicious change and yet maintain the same hash of the modified file. Therefore, the hash of a file can be compared to a stored hash in order to verify that no modifications have been made. In order to prevent an adversary from changing the stored precomputed hash as well as the module being validated, the validity of the stored hash must be verifiable. For example, the stored hash may be cryptographically signed by a trusted signatory.
However, in order to be executed, a file constituting the portable executable (PE) computer program module (e.g. a DLL or EXE) must be loaded into memory. Once loaded, the in-memory module image is subject to potentially malicious modifications such as code patching, detouring and software debugger breakpoints. Consequently, it is necessary to authenticate the in-memory image of the module as well as the on-disk image. Authentication of this in-memory executable image of the file is known as “In-memory authentication” or “dynamic module authentication”.
Since tampering of the in-memory images of application modules can occur at any time during the life of the application session, it is usually desirable from a security standpoint to repeat the in-memory authentication process multiple times.
There are two significant reasons why computing a file hash is not an efficient basis for repeated verification of the in-memory module image. First, working set optimizations may be performed by the operating system or by the program itself which cause not all of a file to be loaded into memory at any given time. This is done to reduce the memory footprint of the running process, for example, so that many processes can run on a computer with limited memory and not cause page swapping. It also minimizes the use of memory bus and device I/O bandwidth (for example, to the hard disk) consumed by subsequent page swapping. Memory and bandwidth are valuable fixed resources dictated by the hardware configuration of the computer, so a reduction in the use of either improves the behavior of the computer on all running applications. However, each time a whole file hash must be computed, the entire file must be loaded into physical memory. This negates the benefit of any working set optimizations performed by the operating system or the program itself because the entire module image is swapped from disk into memory.
A second reason is that a module's in-memory image is not identical to its on-disk image and the differences must be accounted for when comparing to a file hash. For example, relocations may be applied to the in-memory image by the operating system. On-disk, the PE file is stored as if it will be loaded at its preferred base address in memory. However, if there is an address collision with a module that has already been loaded at the preferred address, the operating system must relocate, or “rebase”, the module to a new location. When this rebasing does occur, all addresses which assumed the original preferred address (that is, the “absolute addresses”) within the module image need to change to reflect the base address where the PE file is actually loaded. The PE file contains relocation data which describes where these relocations must be applied. The operating system (OS) loader applies the relocations to the addresses specified in the relocation data. Additionally, the import address table (IAT) in a PE will be modified by the OS loader as function addresses are resolved against DLL export tables. The IAT describes the locations of functions in other modules. When the PE is loaded by the OS, the IAT is changed from the original on-disk version of the PE to reflect the actual locations of functions outside of the PE.
There are multiple methods for performing an in-memory authentication based on a file hash. Each suffers from the inefficiencies mentioned previously. One method is to read the entire on-disk image into memory to compute its hash, compare the hash to the stored signed hash, apply loader changes to the copy and finally compare the copy to the in-memory image. Another method would be to back out loader changes from the in-memory image into another portion of memory, hash that memory and then compare the hash to the signed file hash. Both methods involve three basic tasks: 1) the in-memory image must be swapped into physical memory, 2) a copy of the image must be made in memory and 3) the expected differences between the in-memory image and the on-disk image caused by the operating system loader must be accounted for. These basic tasks are extremely inefficient, in terms of computation, transfer time, and memory allocation.
In view of the foregoing, there is a need for a system that overcomes the drawbacks of the prior art.